package org.microsites.security;

import java.lang.reflect.Method;

import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.beanutils.BeanMap;
import org.microsites.security.Locking.Operation;
import org.microsites.service.LockService;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.microsites.service.GenericService;

public class LockingAdvisor extends StaticMethodMatcherPointcutAdvisor implements MethodInterceptor {

	private static final long serialVersionUID = -26909426724065760L;

	private LockService lockService;

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		Object[] args = mi.getArguments();
		Locking locking = mi.getMethod().getAnnotation(Locking.class);
		Class<?> target = null;
		
		for (Class<?> cls : mi.getThis().getClass().getInterfaces()) {
		    if (!cls.equals(GenericService.class) && GenericService.class.isAssignableFrom(cls)) {
		        target = cls;
		        break;
		    }
		}

		Long id = null;
		if (args != null && args.length > 0) {
			if (args[0] instanceof Long) {
				id = (Long) args[0];
			} else {
				BeanMap bm = new BeanMap(args[0]);
				if (bm.containsKey("id") && bm.get("id") instanceof Long) {
					id = (Long) bm.get("id");
				}
			}
		}

		if (id != null) {
			if (locking.value().equals(Operation.LOCK)) {
				lockService.lock(target, id);
			} else if (locking.value().equals(Operation.KEEP)) {
				lockService.keep(target, id);
			}
		}

		try {
			return mi.proceed();
		} finally {
			if (id != null && locking.value().equals(Operation.RELEASE)) {
				lockService.unlock(target, id);
			}
		}
	}

    @Override
    @SuppressWarnings("rawtypes")
	public boolean matches(Method method, Class cls) {
		return method.getAnnotation(Locking.class) != null;
	}

	@Override
	public Advice getAdvice() {
		return this;
	}

	public LockService getLockService() {
		return lockService;
	}

	public void setLockService(LockService lockService) {
		this.lockService = lockService;
	}

}
